<!DOCTYPE html>
<html lang=zh>
<head>
    <meta charset="utf-8">
    

    <meta name="baidu-site-verification" content="lt822VnP06" />
    <meta name="baidu-site-verification" content="0Ajixw1Puk" />
    <meta name="google-site-verification" content="gCQD0Y6f0YlPTZTAjp_mqms4C7TlkMWrg3Xy0mFdMwI" />
    <title>如何线程安全的使用 HashMap | Giraffe&#39;s Home</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
    <meta name="description" content="这周真是发生了不少事，脑袋和心里一直都很乱，周二参加了一场面试，经历了笔试+3轮面试，周五正式提交了离职申请。要开始新的征程了，意外的有些失落和不舍，毕竟是毕业后的第一份工作，毕竟在这认识了一群可爱的人，毕竟在这学到了很多东西,毕竟这有8000+的aeron chair!!!。可既然已经做了选择就没有退路了，勇敢往下走吧，希望接下来的三周可以把手头上的工作做好交接善始善终，也希望以后不会后悔今天">
<meta property="og:type" content="article">
<meta property="og:title" content="如何线程安全的使用 HashMap">
<meta property="og:url" content="http://yemengying.com/2016/05/07/threadsafe-hashmap/index.html">
<meta property="og:site_name" content="Giraffe's Home">
<meta property="og:description" content="这周真是发生了不少事，脑袋和心里一直都很乱，周二参加了一场面试，经历了笔试+3轮面试，周五正式提交了离职申请。要开始新的征程了，意外的有些失落和不舍，毕竟是毕业后的第一份工作，毕竟在这认识了一群可爱的人，毕竟在这学到了很多东西,毕竟这有8000+的aeron chair!!!。可既然已经做了选择就没有退路了，勇敢往下走吧，希望接下来的三周可以把手头上的工作做好交接善始善终，也希望以后不会后悔今天">
<meta property="og:image" content="http://yemengying.com/images/fighting.jpg">
<meta property="og:updated_time" content="2018-12-13T08:02:59.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="如何线程安全的使用 HashMap">
<meta name="twitter:description" content="这周真是发生了不少事，脑袋和心里一直都很乱，周二参加了一场面试，经历了笔试+3轮面试，周五正式提交了离职申请。要开始新的征程了，意外的有些失落和不舍，毕竟是毕业后的第一份工作，毕竟在这认识了一群可爱的人，毕竟在这学到了很多东西,毕竟这有8000+的aeron chair!!!。可既然已经做了选择就没有退路了，勇敢往下走吧，希望接下来的三周可以把手头上的工作做好交接善始善终，也希望以后不会后悔今天">
<meta name="twitter:image" content="http://yemengying.com/images/fighting.jpg">
    

    

    
        <link rel="icon" href="https://yemengying.com/qiniu/image/image/favicon.png" />
    


    <link rel="stylesheet" href="/lib/font-awesome/css/font-awesome.min.css">
    <link rel="stylesheet" href="/lib/open-sans/styles.css">
    <link rel="stylesheet" href="/lib/source-code-pro/styles.css">

    <link rel="stylesheet" href="/css/style.css">

    <script src="/lib/jquery/2.1.3/jquery.min.js"></script>
    
    
        <link rel="stylesheet" href="/lib/fancybox/jquery.fancybox.css">
    
    
        <script type="text/javascript">
(function(i,s,o,g,r,a,m) {i['GoogleAnalyticsObject']=r;i[r]=i[r]||function() {
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-75861791-1', 'auto');
ga('send', 'pageview');

</script>
    
    
    
        <script>
var _hmt = _hmt || [];
(function() {
    var hm = document.createElement("script");
    hm.src = "//hm.baidu.com/hm.js?44bb8bfb1a576270255713e37746eb82";
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(hm, s);
})();
</script>

    

</head>
<body>
    <script src="//github.elemecdn.com/jiananshi/req/0.1.0/lib/req.js"></script>
    <script src="//npm.elemecdn.com/jinkela@1.2.18/umd.js"></script>
    <script src="//github.elemecdn.com/jiananshi/DisqusJS/2.0.8/index.js"></script>
    <div id="container">
        <header id="header">
    <div id="header-main" class="header-inner">
        <div class="outer">
            <a href="/" id="logo">
                <i class="logo"></i>
                <span class="site-title">Giraffe&#39;s Home</span>
            </a>
            <nav id="main-nav">
                
                    <a class="main-nav-link" href="/.">首页</a>
                
                    <a class="main-nav-link" href="/archives">归档</a>
                
                    <a class="main-nav-link" href="/categories">分类</a>
                
                    <a class="main-nav-link" href="/tags">标签</a>
                
                    <a class="main-nav-link" href="/about">关于</a>
                
                    <a class="main-nav-link" href="/message">留言</a>
                
                    <a class="main-nav-link" href="/friends">友链</a>
                
                    <a class="main-nav-link" href="/reading">正在读...</a>
                
            </nav>
            
                
                <nav id="sub-nav">
                    <div class="profile" id="profile-nav">
                        <a id="profile-anchor" href="javascript:;">
                            <img class="avatar" src="https://pic.yupoo.com/jiananshi/e85e4303/735cf286.jpeg" />
                            <i class="fa fa-caret-down"></i>
                        </a>
                    </div>
                </nav>
            
            <div id="search-form-wrap">

    <form class="search-form">
        <input type="text" class="ins-search-input search-form-input" placeholder="搜索" />
        <button type="submit" class="search-form-submit"></button>
    </form>
    <div class="ins-search">
    <div class="ins-search-mask"></div>
    <div class="ins-search-container">
        <div class="ins-input-wrapper">
            <input type="text" class="ins-search-input" placeholder="想要查找什么..." />
            <span class="ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
        </div>
        <div class="ins-section-wrapper">
            <div class="ins-section-container"></div>
        </div>
    </div>
</div>
<script>
(function (window) {
    var INSIGHT_CONFIG = {
        TRANSLATION: {
            POSTS: '文章',
            PAGES: '页面',
            CATEGORIES: '分类',
            TAGS: '标签',
            UNTITLED: '(未命名)',
        },
        ROOT_URL: '/',
        CONTENT_URL: '/content.json',
    };
    window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>
<script src="/js/insight.js"></script>

</div>
        </div>
    </div>
    <div id="main-nav-mobile" class="header-sub header-inner">
        <table class="menu outer">
            <tr>
                
                    <td><a class="main-nav-link" href="/.">首页</a></td>
                
                    <td><a class="main-nav-link" href="/archives">归档</a></td>
                
                    <td><a class="main-nav-link" href="/categories">分类</a></td>
                
                    <td><a class="main-nav-link" href="/tags">标签</a></td>
                
                    <td><a class="main-nav-link" href="/about">关于</a></td>
                
                    <td><a class="main-nav-link" href="/message">留言</a></td>
                
                    <td><a class="main-nav-link" href="/friends">友链</a></td>
                
                    <td><a class="main-nav-link" href="/reading">正在读...</a></td>
                
                <td>
                    
    <div class="search-form">
        <input type="text" class="ins-search-input search-form-input" placeholder="搜索" />
    </div>

                </td>
            </tr>
        </table>
    </div>
</header>

        <div class="outer">
            
                

<aside id="profile">
    <div class="inner profile-inner">
        <div class="base-info profile-block">
            <img id="avatar" src="https://pic.yupoo.com/jiananshi/e85e4303/735cf286.jpeg" />
            <h2 id="name">Giraffe</h2>
            <h3 id="title">Java Developer</h3>
            <span id="location"><i class="fa fa-map-marker"></i>Shanghai, China</span>
            <a id="follow" target="_blank" href="https://github.com/giraffe0813/">关注我</a>
        </div>
        <div class="article-info profile-block">
            <div class="article-info-block">
                56
                <span>文章</span>
            </div>
            <div class="article-info-block">
                36
                <span>标签</span>
            </div>
        </div>
        <div class="profile-block recent-comments">
            <p class="recent-comments-title">最新评论</p>
            <ul id="disqus-recent-comments" class="recent-comments-container">
            </ul>
        </div>
        
        <div class="profile-block social-links">
            <table>
                <tr>
                    
                    
                    <td>
                        <a href="http://github.com/giraffe0813" target="_blank" title="github" class=tooltip>
                            <i class="fa fa-github"></i>
                        </a>
                    </td>
                    
                    <td>
                        <a href="/atom.xml" target="_blank" title="rss" class=tooltip>
                            <i class="fa fa-rss"></i>
                        </a>
                    </td>
                    
                </tr>
            </table>
        </div>
        
    </div>
</aside>

            
            <section id="main"><article id="2016/05/07/threadsafe-hashmap/" class="article article-type-post" itemscope itemprop="blogPost">
    <div class="article-inner">
        
        
        
            <header class="article-header">
                
    
        <h1 class="article-title" itemprop="name">
            如何线程安全的使用 HashMap
        </h1>
    

                <div class="article-meta">
                    
    <div class="article-date">
        <i class="fa fa-calendar"></i>
        <a href="/2016/05/07/threadsafe-hashmap/">
            <time datetime="2016-05-07T09:21:06.000Z" itemprop="datePublished">2016-05-07</time>
        </a>
    </div>


                    
    <div class="article-category">
    	<i class="fa fa-folder"></i>
        <a class="article-category-link" href="/categories/java/">java</a><i class="fa fa-angle-right"></i><a class="article-category-link" href="/categories/java/hashmap/">hashmap</a>
    </div>

                    
    <div class="article-tag">
        <i class="fa fa-tag"></i>
        <a class="tag-link" href="/tags/hashMap/">hashMap</a>, <a class="tag-link" href="/tags/java/">java</a>
    </div>

                </div>
            </header>
        
        <div class="article-entry" itemprop="articleBody">
        
            
                <div id="toc" class="toc-article">
                <strong class="toc-title">文章目录</strong>
                    <ol class="toc"><li class="toc-item toc-level-3"><a class="toc-link" href="#为什么HashMap是线程不安全的"><span class="toc-number">1.</span> <span class="toc-text">为什么HashMap是线程不安全的</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#HashMap的内部存储结构"><span class="toc-number">1.1.</span> <span class="toc-text">HashMap的内部存储结构</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#HashMap的自动扩容机制"><span class="toc-number">1.2.</span> <span class="toc-text">HashMap的自动扩容机制</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#为什么线程不安全"><span class="toc-number">1.3.</span> <span class="toc-text">为什么线程不安全</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#如何线程安全的使用HashMap"><span class="toc-number">2.</span> <span class="toc-text">如何线程安全的使用HashMap</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Hashtable"><span class="toc-number">2.1.</span> <span class="toc-text">Hashtable</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ConcurrentHashMap"><span class="toc-number">2.2.</span> <span class="toc-text">ConcurrentHashMap</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#SynchronizedMap"><span class="toc-number">2.3.</span> <span class="toc-text">SynchronizedMap</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#性能对比"><span class="toc-number">2.4.</span> <span class="toc-text">性能对比</span></a></li></ol></li></ol>
                </div>
            
            <blockquote>
<p>这周真是发生了不少事，脑袋和心里一直都很乱，周二参加了一场面试，经历了笔试+3轮面试，周五正式提交了离职申请。要开始新的征程了，意外的有些失落和不舍，毕竟是毕业后的第一份工作，毕竟在这认识了一群可爱的人，毕竟在这学到了很多东西,毕竟这有8000+的aeron chair!!!。可既然已经做了选择就没有退路了，勇敢往下走吧，希望接下来的三周可以把手头上的工作做好交接善始善终，也希望以后不会后悔今天的选择。</p>
</blockquote>
<a id="more"></a>
<p><img src="https://pic.yupoo.com/jiananshi/0586dba8/fdf8482e.jpg" alt="fighting！！！">    </p>
<p>进入正题，在周二面试时，一面的面试官有问到 HashMap 是否是线程安全的，如何在线程安全的前提下使用 HashMap,其实也就是 <code>HashMap</code>，<code>Hashtable</code>，<code>ConcurrentHashMap</code> 和 <code>synchronized Map</code> 的原理和区别。当时有些紧张只是简单说了下HashMap不是线程安全的；Hashtable 线程安全，但效率低，因为是 Hashtable 是使用 synchronized 的，所有线程竞争同一把锁；而 ConcurrentHashMap 不仅线程安全而且效率高，因为它包含一个 segment 数组，将数据分段存储，给每一段数据配一把锁，也就是所谓的锁分段技术。当时忘记了 synchronized Map 和解释一下 HashMap 为什么线程不安全。面试结束后问了下面试官哪里有些不足，面试官说上面这个问题的回答算过关，但可以在深入一些或者自己动手尝试一下。so~~~虽然拿到了 offer，但还是再整理一下，不能得过且过啊。</p>
<h3 id="为什么HashMap是线程不安全的"><a href="#为什么HashMap是线程不安全的" class="headerlink" title="为什么HashMap是线程不安全的"></a>为什么HashMap是线程不安全的</h3><p>总说 HashMap 是线程不安全的，不安全的，不安全的，那么到底为什么它是线程不安全的呢？要回答这个问题就要先来简单了解一下 HashMap 源码中的使用的<code>存储结构</code>(这里引用的是 Java 8 的源码，与7是不一样的)和它的<code>扩容机制</code>。</p>
<h4 id="HashMap的内部存储结构"><a href="#HashMap的内部存储结构" class="headerlink" title="HashMap的内部存储结构"></a>HashMap的内部存储结构</h4><p>下面是 HashMap 使用的存储结构:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">transient</span> Node&lt;K,V&gt;[] table;</div><div class="line"></div><div class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Node</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; <span class="keyword">implements</span> <span class="title">Map</span>.<span class="title">Entry</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt; </span>&#123;</div><div class="line">        <span class="keyword">final</span> <span class="keyword">int</span> hash;</div><div class="line">        <span class="keyword">final</span> K key;</div><div class="line">        V value;</div><div class="line">        Node&lt;K,V&gt; next;</div><div class="line">&#125;</div></pre></td></tr></table></figure></p>
<p>可以看到 HashMap 内部存储使用了一个 Node 数组(默认大小是16)，而 Node 类包含一个类型为 Node 的 next 的变量，也就是相当于一个链表，所有根据 hash 值计算的 bucket 一样的 key 会存储到同一个链表里(即产生了冲突)，大概就是下面图的样子(顺便推荐个在线画图的网站<a href="http://creately.com/Draw-UML-and-Class-Diagrams-Online" target="_blank" rel="external">Creately</a>)。<br><img src="https://pic.yupoo.com/jiananshi/e578c267/c8d4d250.png" alt="HashMap内部存储结果"></p>
<blockquote>
<p>需要注意的是，在 Java 8 中如果 hash 值相同的 key 数量大于指定值(默认是8)时使用平衡树来代替链表，这会将get()方法的性能从O(n)提高到O(logn)。具体的可以看我的另一篇博客<a href="http://yemengying.com/2016/02/03/%E8%AF%91-Java%E4%B8%ADHashMap%E5%92%8CLinkedHashMap%E5%A6%82%E4%BD%95%E8%A7%A3%E5%86%B3%E5%86%B2%E7%AA%81/">Java 8中HashMap和LinkedHashMap如何解决冲突</a>。</p>
</blockquote>
<h4 id="HashMap的自动扩容机制"><a href="#HashMap的自动扩容机制" class="headerlink" title="HashMap的自动扩容机制"></a>HashMap的自动扩容机制</h4><p>HashMap 内部的 Node 数组默认的大小是16，假设有100万个元素，那么最好的情况下每个 hash 桶里都有62500个元素😱，这时get(),put(),remove()等方法效率都会降低。为了解决这个问题，HashMap 提供了自动扩容机制，当元素个数达到数组大小<em> loadFactor 后会扩大数组的大小，在默认情况下，数组大小为16，loadFactor 为0.75，也就是说当 HashMap 中的元素超过16\</em>0.75=12时，会把数组大小扩展为2*16=32，并且重新计算每个元素在新数组中的位置。如下图所示(<a href="http://coding-geek.com/how-does-a-hashmap-work-in-java/" target="_blank" rel="external">图片来源</a>，权侵删)。<br><img src="https://pic.yupoo.com/jiananshi/ce749998/37356c77.jpg" alt="自动扩容"><br>从图中可以看到没扩容前，获取 EntryE 需要遍历5个元素，扩容之后只需要2次。</p>
<h4 id="为什么线程不安全"><a href="#为什么线程不安全" class="headerlink" title="为什么线程不安全"></a>为什么线程不安全</h4><p>个人觉得 HashMap 在并发时可能出现的问题主要是两方面,首先如果多个线程同时使用put方法添加元素，而且假设正好存在两个 put 的 key 发生了碰撞(根据 hash 值计算的 bucket 一样)，那么根据 HashMap 的实现，这两个 key 会添加到数组的同一个位置，这样最终就会发生其中一个线程的 put 的数据被覆盖。第二就是如果多个线程同时检测到元素个数超过数组大小* loadFactor ，这样就会发生多个线程同时对 Node 数组进行扩容，都在重新计算元素位置以及复制数据，但是最终只有一个线程扩容后的数组会赋给 table，也就是说其他线程的都会丢失，并且各自线程 put 的数据也丢失。<br>关于 HashMap 线程不安全这一点，《Java并发编程的艺术》一书中是这样说的：</p>
<blockquote>
<p>HashMap 在并发执行 put 操作时会引起死循环，导致 CPU 利用率接近100%。因为多线程会导致 HashMap 的 Node 链表形成环形数据结构，一旦形成环形数据结构，Node 的 next 节点永远不为空，就会在获取 Node 时产生死循环。</p>
</blockquote>
<p>哇塞，听上去si不si好神奇，居然会产生死循环。。。。 google 了一下，才知道死循环并不是发生在 put 操作时，而是发生在扩容时。详细的解释可以看下面几篇博客：</p>
<ul>
<li><a href="http://coolshell.cn/articles/9606.html" target="_blank" rel="external">酷壳-Java HashMap的死循环</a></li>
<li><a href="http://firezhfox.iteye.com/blog/2241043" target="_blank" rel="external">HashMap在java并发中如何发生死循环</a></li>
<li><a href="http://coding-geek.com/how-does-a-hashmap-work-in-java/" target="_blank" rel="external">How does a HashMap work in JAVA</a></li>
</ul>
<h3 id="如何线程安全的使用HashMap"><a href="#如何线程安全的使用HashMap" class="headerlink" title="如何线程安全的使用HashMap"></a>如何线程安全的使用HashMap</h3><p>了解了 HashMap 为什么线程不安全，那现在看看如何线程安全的使用 HashMap。这个无非就是以下三种方式：</p>
<ul>
<li>Hashtable</li>
<li>ConcurrentHashMap</li>
<li>Synchronized Map</li>
</ul>
<p>例子：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//Hashtable</span></div><div class="line">Map&lt;String, String&gt; hashtable = <span class="keyword">new</span> Hashtable&lt;&gt;();</div><div class="line"></div><div class="line"><span class="comment">//synchronizedMap</span></div><div class="line">Map&lt;String, String&gt; synchronizedHashMap = Collections.synchronizedMap(<span class="keyword">new</span> HashMap&lt;String, String&gt;());</div><div class="line"></div><div class="line"><span class="comment">//ConcurrentHashMap</span></div><div class="line">Map&lt;String, String&gt; concurrentHashMap = <span class="keyword">new</span> ConcurrentHashMap&lt;&gt;();</div></pre></td></tr></table></figure></p>
<p>依次来看看。</p>
<h4 id="Hashtable"><a href="#Hashtable" class="headerlink" title="Hashtable"></a>Hashtable</h4><p>先稍微吐槽一下，为啥命名不是 HashTable 啊，看着好难受😖，不管了就装作它叫HashTable 吧。这货已经不常用了，就简单说说吧。HashTable 源码中是使用 <code>synchronized</code> 来保证线程安全的，比如下面的 get 方法和 put 方法：<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>&#123;</div><div class="line">       <span class="comment">// 省略实现</span></div><div class="line">    &#125;</div><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</div><div class="line">	<span class="comment">// 省略实现</span></div><div class="line">    &#125;</div></pre></td></tr></table></figure></p>
<p>所以当一个线程访问 HashTable 的同步方法时，其他线程如果也要访问同步方法，会被阻塞住。举个例子，当一个线程使用 put 方法时，另一个线程不但不可以使用 put 方法，连 get 方法都不可以，好霸道啊！！！so~~，效率很低，现在基本不会选择它了。</p>
<h4 id="ConcurrentHashMap"><a href="#ConcurrentHashMap" class="headerlink" title="ConcurrentHashMap"></a>ConcurrentHashMap</h4><p>ConcurrentHashMap (以下简称CHM)是 JUC 包中的一个类，Spring 的源码中有很多使用 CHM 的地方。之前已经翻译过一篇关于 ConcurrentHashMap 的博客，<a href="http://yemengying.com/2015/11/06/%E3%80%90%E8%AF%91%E3%80%91%E5%A6%82%E4%BD%95%E5%9C%A8java%E4%B8%AD%E4%BD%BF%E7%94%A8ConcurrentHashMap/">如何在java中使用ConcurrentHashMap</a>，里面介绍了 CHM 在 Java 中的实现，CHM 的一些重要特性和什么情况下应该使用 CHM。需要注意的是，上面博客是基于 Java 7 的，和8有区别,在8中 CHM 摒弃了 Segment（锁段）的概念，而是启用了一种全新的方式实现,利用 CAS 算法，有时间会重新总结一下。</p>
<h4 id="SynchronizedMap"><a href="#SynchronizedMap" class="headerlink" title="SynchronizedMap"></a>SynchronizedMap</h4><p>看了一下源码，SynchronizedMap 的实现还是很简单的。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// synchronizedMap方法</span></div><div class="line"><span class="keyword">public</span> <span class="keyword">static</span> &lt;K,V&gt; <span class="function">Map&lt;K,V&gt; <span class="title">synchronizedMap</span><span class="params">(Map&lt;K,V&gt; m)</span> </span>&#123;</div><div class="line">       <span class="keyword">return</span> <span class="keyword">new</span> SynchronizedMap&lt;&gt;(m);</div><div class="line">   &#125;</div><div class="line"><span class="comment">// SynchronizedMap类</span></div><div class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">SynchronizedMap</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt;</span></div><div class="line">       <span class="keyword">implements</span> <span class="title">Map</span>&lt;<span class="title">K</span>,<span class="title">V</span>&gt;, <span class="title">Serializable</span> &#123;</div><div class="line">       <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">1978198479659022715L</span>;</div><div class="line"></div><div class="line">       <span class="keyword">private</span> <span class="keyword">final</span> Map&lt;K,V&gt; m;     <span class="comment">// Backing Map</span></div><div class="line">       <span class="keyword">final</span> Object      mutex;        <span class="comment">// Object on which to synchronize</span></div><div class="line"></div><div class="line">       SynchronizedMap(Map&lt;K,V&gt; m) &#123;</div><div class="line">           <span class="keyword">this</span>.m = Objects.requireNonNull(m);</div><div class="line">           mutex = <span class="keyword">this</span>;</div><div class="line">       &#125;</div><div class="line"></div><div class="line">       SynchronizedMap(Map&lt;K,V&gt; m, Object mutex) &#123;</div><div class="line">           <span class="keyword">this</span>.m = m;</div><div class="line">           <span class="keyword">this</span>.mutex = mutex;</div><div class="line">       &#125;</div><div class="line"></div><div class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</div><div class="line">           <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> m.size();&#125;</div><div class="line">       &#125;</div><div class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isEmpty</span><span class="params">()</span> </span>&#123;</div><div class="line">           <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> m.isEmpty();&#125;</div><div class="line">       &#125;</div><div class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">containsKey</span><span class="params">(Object key)</span> </span>&#123;</div><div class="line">           <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> m.containsKey(key);&#125;</div><div class="line">       &#125;</div><div class="line">       <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">containsValue</span><span class="params">(Object value)</span> </span>&#123;</div><div class="line">           <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> m.containsValue(value);&#125;</div><div class="line">       &#125;</div><div class="line">       <span class="function"><span class="keyword">public</span> V <span class="title">get</span><span class="params">(Object key)</span> </span>&#123;</div><div class="line">           <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> m.get(key);&#125;</div><div class="line">       &#125;</div><div class="line"></div><div class="line">       <span class="function"><span class="keyword">public</span> V <span class="title">put</span><span class="params">(K key, V value)</span> </span>&#123;</div><div class="line">           <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> m.put(key, value);&#125;</div><div class="line">       &#125;</div><div class="line">       <span class="function"><span class="keyword">public</span> V <span class="title">remove</span><span class="params">(Object key)</span> </span>&#123;</div><div class="line">           <span class="keyword">synchronized</span> (mutex) &#123;<span class="keyword">return</span> m.remove(key);&#125;</div><div class="line">       &#125;</div><div class="line">       <span class="comment">// 省略其他方法</span></div><div class="line">   &#125;</div></pre></td></tr></table></figure></p>
<p>从源码中可以看出调用 synchronizedMap() 方法后会返回一个 SynchronizedMap 类的对象，而在 SynchronizedMap 类中使用了 synchronized 同步关键字来保证对 Map 的操作是线程安全的。</p>
<h4 id="性能对比"><a href="#性能对比" class="headerlink" title="性能对比"></a>性能对比</h4><p>这是要靠数据说话的时代，所以不能只靠嘴说 CHM 快，它就快了。写个测试用例，实际的比较一下这三种方式的效率(<a href="http://crunchify.com/hashmap-vs-concurrenthashmap-vs-synchronizedmap-how-a-hashmap-can-be-synchronized-in-java/" target="_blank" rel="external">源码来源</a>)，下面的代码分别通过三种方式创建 Map 对象，使用 <code>ExecutorService</code> 来并发运行5个线程，每个线程添加/获取500K个元素。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CrunchifyConcurrentHashMapVsSynchronizedMap</span> </span>&#123;</div><div class="line"></div><div class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">static</span> <span class="keyword">int</span> THREAD_POOL_SIZE = <span class="number">5</span>;</div><div class="line"></div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Map&lt;String, Integer&gt; crunchifyHashTableObject = <span class="keyword">null</span>;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Map&lt;String, Integer&gt; crunchifySynchronizedMapObject = <span class="keyword">null</span>;</div><div class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Map&lt;String, Integer&gt; crunchifyConcurrentHashMapObject = <span class="keyword">null</span>;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</div><div class="line"></div><div class="line">        <span class="comment">// Test with Hashtable Object</span></div><div class="line">        crunchifyHashTableObject = <span class="keyword">new</span> Hashtable&lt;&gt;();</div><div class="line">        crunchifyPerformTest(crunchifyHashTableObject);</div><div class="line"></div><div class="line">        <span class="comment">// Test with synchronizedMap Object</span></div><div class="line">        crunchifySynchronizedMapObject = Collections.synchronizedMap(<span class="keyword">new</span> HashMap&lt;String, Integer&gt;());</div><div class="line">        crunchifyPerformTest(crunchifySynchronizedMapObject);</div><div class="line"></div><div class="line">        <span class="comment">// Test with ConcurrentHashMap Object</span></div><div class="line">        crunchifyConcurrentHashMapObject = <span class="keyword">new</span> ConcurrentHashMap&lt;&gt;();</div><div class="line">        crunchifyPerformTest(crunchifyConcurrentHashMapObject);</div><div class="line"></div><div class="line">    &#125;</div><div class="line"></div><div class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">crunchifyPerformTest</span><span class="params">(<span class="keyword">final</span> Map&lt;String, Integer&gt; crunchifyThreads)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</div><div class="line"></div><div class="line">        System.out.println(<span class="string">"Test started for: "</span> + crunchifyThreads.getClass());</div><div class="line">        <span class="keyword">long</span> averageTime = <span class="number">0</span>;</div><div class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</div><div class="line"></div><div class="line">            <span class="keyword">long</span> startTime = System.nanoTime();</div><div class="line">            ExecutorService crunchifyExServer = Executors.newFixedThreadPool(THREAD_POOL_SIZE);</div><div class="line"></div><div class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; THREAD_POOL_SIZE; j++) &#123;</div><div class="line">                crunchifyExServer.execute(<span class="keyword">new</span> Runnable() &#123;</div><div class="line">                    <span class="meta">@SuppressWarnings</span>(<span class="string">"unused"</span>)</div><div class="line">                    <span class="meta">@Override</span></div><div class="line">                    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</div><div class="line"></div><div class="line">                        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">500000</span>; i++) &#123;</div><div class="line">                            Integer crunchifyRandomNumber = (<span class="keyword">int</span>) Math.ceil(Math.random() * <span class="number">550000</span>);</div><div class="line"></div><div class="line">                            <span class="comment">// Retrieve value. We are not using it anywhere</span></div><div class="line">                            Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber));</div><div class="line"></div><div class="line">                            <span class="comment">// Put value</span></div><div class="line">                            crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber);</div><div class="line">                        &#125;</div><div class="line">                    &#125;</div><div class="line">                &#125;);</div><div class="line">            &#125;</div><div class="line"></div><div class="line">            <span class="comment">// Make sure executor stops</span></div><div class="line">            crunchifyExServer.shutdown();</div><div class="line"></div><div class="line">            <span class="comment">// Blocks until all tasks have completed execution after a shutdown request</span></div><div class="line">            crunchifyExServer.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);</div><div class="line"></div><div class="line">            <span class="keyword">long</span> entTime = System.nanoTime();</div><div class="line">            <span class="keyword">long</span> totalTime = (entTime - startTime) / <span class="number">1000000L</span>;</div><div class="line">            averageTime += totalTime;</div><div class="line">            System.out.println(<span class="string">"2500K entried added/retrieved in "</span> + totalTime + <span class="string">" ms"</span>);</div><div class="line">        &#125;</div><div class="line">        System.out.println(<span class="string">"For "</span> + crunchifyThreads.getClass() + <span class="string">" the average time is "</span> + averageTime / <span class="number">5</span> + <span class="string">" ms\n"</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>测试结果：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line">Test started <span class="keyword">for</span>: class java.util.Hashtable</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 2018 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 1746 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 1806 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 1801 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 1804 ms</div><div class="line">For class java.util.Hashtable the average time is 1835 ms</div><div class="line"></div><div class="line">Test started <span class="keyword">for</span>: class java.util.Collections<span class="variable">$SynchronizedMap</span></div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 3041 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 1690 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 1740 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 1649 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 1696 ms</div><div class="line">For class java.util.Collections<span class="variable">$SynchronizedMap</span> the average time is 1963 ms</div><div class="line"></div><div class="line">Test started <span class="keyword">for</span>: class java.util.concurrent.ConcurrentHashMap</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 738 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 696 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 548 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 1447 ms</div><div class="line">2500K entried added/retrieved <span class="keyword">in</span> 531 ms</div><div class="line">For class java.util.concurrent.ConcurrentHashMap the average time is 792 ms</div></pre></td></tr></table></figure>
<p>这个就不用废话了，CHM 性能是明显优于 Hashtable 和 SynchronizedMap 的,CHM 花费的时间比前两个的一半还少，哈哈，以后再有人问就可以甩数据了。</p>
<blockquote>
<p>欢迎指正错误，欢迎一起讨论。另外，针对提离职当天发生的一个小插曲，真真是给我上了一课，不是所有人都能接受实话的，只想引用欢乐颂里安迪的一句话:常与同好争高下,不与傻瓜论短长。</p>
</blockquote>
<p>都看到这了，关注个公众号再走吧🙈<br><img src="https://pic.yupoo.com/jiananshi/3302bb60/7b8457af.jpg" alt="Running Geek"></p>

        
        </div>
        <footer class="article-footer">
            <div class="share-container">



</div>

    <a data-url="http://yemengying.com/2016/05/07/threadsafe-hashmap/" data-id="cjtvbucjn0048z15sq665g09h" class="article-share-link"><i class="fa fa-share"></i>分享到</a>
<script>
    (function ($) {
        // Prevent duplicate binding
        if (typeof(__SHARE_BUTTON_BINDED__) === 'undefined' || !__SHARE_BUTTON_BINDED__) {
            __SHARE_BUTTON_BINDED__ = true;
        } else {
            return;
        }
        $('body').on('click', function() {
            $('.article-share-box.on').removeClass('on');
        }).on('click', '.article-share-link', function(e) {
            e.stopPropagation();

            var $this = $(this),
                url = $this.attr('data-url'),
                encodedUrl = encodeURIComponent(url),
                id = 'article-share-box-' + $this.attr('data-id'),
                offset = $this.offset(),
                box;

            if ($('#' + id).length) {
                box = $('#' + id);

                if (box.hasClass('on')){
                    box.removeClass('on');
                    return;
                }
            } else {
                var html = [
                    '<div id="' + id + '" class="article-share-box">',
                        '<input class="article-share-input" value="' + url + '">',
                        '<div class="article-share-links">',
                            '<a href="https://twitter.com/intent/tweet?url=' + encodedUrl + '" class="fa fa-twitter article-share-twitter" target="_blank" title="Twitter"></a>',
                            '<a href="https://www.facebook.com/sharer.php?u=' + encodedUrl + '" class="fa fa-facebook article-share-facebook" target="_blank" title="Facebook"></a>',
                            '<a href="http://pinterest.com/pin/create/button/?url=' + encodedUrl + '" class="fa fa-pinterest article-share-pinterest" target="_blank" title="Pinterest"></a>',
                            '<a href="https://plus.google.com/share?url=' + encodedUrl + '" class="fa fa-google article-share-google" target="_blank" title="Google+"></a>',
                        '</div>',
                    '</div>'
                ].join('');

              box = $(html);

              $('body').append(box);
            }

            $('.article-share-box.on').hide();

            box.css({
                top: offset.top + 25,
                left: offset.left
            }).addClass('on');

        }).on('click', '.article-share-box', function (e) {
            e.stopPropagation();
        }).on('click', '.article-share-box-input', function () {
            $(this).select();
        }).on('click', '.article-share-box-link', function (e) {
            e.preventDefault();
            e.stopPropagation();

            window.open(this.href, 'article-share-box-window-' + Date.now(), 'width=500,height=450');
        });
    })(jQuery);
</script>

            
    
        <a href="http://yemengying.com/2016/05/07/threadsafe-hashmap/#comments" class="article-comment-link disqus-comment-count" data-disqus-url="http://yemengying.com/2016/05/07/threadsafe-hashmap/">评论</a>
    

        </footer>
    </div>
    
        
<nav id="article-nav">
    
        <a href="/2016/05/13/jvm-GC/" id="article-nav-newer" class="article-nav-link-wrap">
            <strong class="article-nav-caption">上一篇</strong>
            <div class="article-nav-title">
                
                    【译】Java中的垃圾回收机制
                
            </div>
        </a>
    
    
        <a href="/2016/04/24/good-website/" id="article-nav-older" class="article-nav-link-wrap">
            <strong class="article-nav-caption">下一篇</strong>
            <div class="article-nav-title">觉得还不错的网站</div>
        </a>
    
</nav>


    
    <script>
   DisqusJS.getArticleComments();
   </script>
</article>


    
    <section id="comments">
    
        
    <div id="disqus_thread">
        <noscript>Please enable JavaScript to view the <a href="//disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
    </div>

    
    </section>

</section>
            
        </div>
        <footer id="footer">
    <div class="outer">
        <div id="footer-info" class="inner">
            &copy; 2019 Mengying Ye<br>
            Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
        </div>
    </div>
</footer>
        
    
   <script>
   DisqusJS.getRecentComments(document.querySelector('#disqus-recent-comments'));
   </script>



    
        <script src="/lib/fancybox/jquery.fancybox.pack.js"></script>
    


<!-- Custom Scripts -->
<script src="/js/main.js"></script>

    </div>
</body>
</html>
